#pragma once
#include "LogMessage.hpp"

#include <iostream>
#include <string>
#include <memory>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <unistd.h>

// 对套接字相关的接口进行封装
class Sock
{
private:
    static const int gBackLog = 20;

public:
        // 1. 创建套接字
    static int Socket()
    {
             /*先AF_INET确定网络通信*/  /*这里用的是TCP，所以用SOCK_STREAM*/
        int listenSock = socket(AF_INET, SOCK_STREAM, 0);
            // 创建失败返回-1
        if(listenSock == -1)
        {
            LogMessage(FATAL, _F, _L, "server create socket fail");
            exit(2);
        }
        LogMessage(DEBUG, _F, _L, "server create socket success, listen sock::%d", listenSock);


        // 创建成功
        return listenSock;
    }

        // 2. bind 绑定IP和port
    static void Bind(int listenSock, uint16_t port, const std::string& ip = "0.0.0.0")
    {
        sockaddr_in local; // 各个字段填充
        memset(&local, 0, sizeof(local));
                                        // 若为空字符串就绑定当前主机所有IP
        local.sin_addr.s_addr = inet_addr(ip.c_str());
        local.sin_port = htons(port);
        local.sin_family = AF_INET;
                                            /*填充好了绑定*/
        if(bind(listenSock, reinterpret_cast<sockaddr*>(&local), sizeof(local)) < 0)
        {
            LogMessage(FATAL, _F, _L, "server bind IP+port fail :: %d:%s", errno, strerror(errno));
            exit(3);
        }
        LogMessage(DEBUG, _F, _L, "server bind IP+port success");
    }

        // 3. listen为套接字设置监听状态
    static void Listen(int listenSock)
    {
        if(listen(listenSock, gBackLog/*后面再详谈listen第二个参数*/) < 0)
        {
            LogMessage(FATAL, _F, _L, "srever listen fail");
            exit(4);
        }
        LogMessage(NORMAL, _F, _L, "server init success");
    }

        // 4.accept接收连接           输出型参数，返回客户端的IP + port
    static int Accept(int listenSock, std::string &clientIp, uint16_t &clientPort)
    {
            /*客户端相关字段*/
        sockaddr_in clientMessage;
        socklen_t clientLen = sizeof(clientMessage);
        memset(&clientMessage, 0, clientLen);
        // 接收连接
        int serverSock = accept(listenSock, reinterpret_cast<sockaddr*>(&clientMessage), &clientLen);

        // 对端的IP和port信息
        clientIp = inet_ntoa(clientMessage.sin_addr);
        clientPort = ntohs(clientMessage.sin_port);

        if(serverSock < 0)
        {
            // 这里没连接上不能说直接退出，就像张三没有揽到某个客人餐馆就不干了，所以日志等级为ERROR
            LogMessage(ERROR, _F, _L, "server accept connection fail");
            return -1;
        }
        else
        {
            LogMessage(NORMAL, _F, _L, "server accept connection success ::[%s:%d] server sock::%d", \
                                                                clientIp.c_str(), clientPort,serverSock);
        }

        return serverSock;
    }

};